/* 
 * Copyright (c) 2006 Israfil Consulting Services Corporation
 * Copyright (c) 2006 Christian Edward Gruber
 * All Rights Reserved
 *
 * $Id: Types.java 644 2009-01-04 18:02:21Z christianedwardgruber $
 */
package net.israfil.foundation.core;

/**
 * A Utility class that provides type-conversion facilities.  For
 * example, this class provides well-deliniated logic for taking 
 * a generic object and converting it into another concrete type, such
 * as a String.  Another example would be transforming the string "yes"
 * into a boolean in a well-defined, well-ordered way.
 * 
 * @author <a href="mailto:cgruber@israfil.net">Christian Edward Gruber</a>
 * @author Latest: $Author: christianedwardgruber $
 * @version $Revision: 644 $
 */
public class Types {
	
	private static final byte BOOLEAN_FALSE_NUMERIC = 0;
	private static final byte BOOLEAN_TRUE_NUMERIC = 1;
	
	/**
	 * A delegate interface that, if provided to the convert(String,Class,Converter) 
	 * method will add fallback conversions.  This allows the existing
	 * convert method to be extended without subclassing, overriding, or 
	 * modifying source.
	 * @author <a href="mailto:cgruber@israfil.net">Christian Edward Gruber</a>
	 */
	public static interface Converter {
		public Object convert(Object val, Class<?> aClass);
	}

	/**
	 * A method to convert an object to another object of a given type.
	 * @param value The object to be converted.
	 * @param aClass The class to which you wish to convert the object.
	 * @return The converted object.
	 */
	public static Object convert(Object value, Class<?> aClass) {
		return convert(value,aClass,null);
	}
	
	/**
	 * A method to convert an object to another object of a given type.
	 * @param value The object to be converted.
	 * @param aClass The class to which you wish to convert the object.
	 * @param converter An optional delegate that will convert the object if it fails - for extensibility.
	 * @return The converted object.
	 */
	public static Object convert(Object value, Class<?> aClass,
            Converter converter) {
		if (value == null) return null;
		if (aClass == null) throw new IllegalArgumentException("Cannot convert to a null class.");
		if (Void.class.isAssignableFrom(aClass)) throw new IllegalArgumentException("Cannot convert to void.");
		if (aClass.isInstance(value) ) return value;
		
		if (aClass.isAssignableFrom(String.class)) {

			// TODO: Insert any specific conversions
			return String.valueOf(value);
		} else if (Boolean.class.isAssignableFrom(aClass)) {
			if (value instanceof String) {
				if (((String)value).equalsIgnoreCase("Yes")) return Boolean.TRUE;
				if (((String)value).equalsIgnoreCase("True")) return Boolean.TRUE;
				if (((String)value).equalsIgnoreCase("Y")) return Boolean.TRUE;
				if (((String)value).equalsIgnoreCase("T")) return Boolean.TRUE;
				if (((String)value).equalsIgnoreCase("1")) return Boolean.TRUE;
				if (((String)value).equalsIgnoreCase("Aye")) return Boolean.TRUE;
				if (((String)value).equalsIgnoreCase("Yar")) return Boolean.TRUE;
				if (((String)value).equalsIgnoreCase("No")) return Boolean.FALSE;
				if (((String)value).equalsIgnoreCase("False")) return Boolean.FALSE;
				if (((String)value).equalsIgnoreCase("N")) return Boolean.FALSE;
				if (((String)value).equalsIgnoreCase("F")) return Boolean.FALSE;
				if (((String)value).equalsIgnoreCase("0")) return Boolean.FALSE;
				if (((String)value).equalsIgnoreCase("Avast")) return Boolean.FALSE;
				if (((String)value).equalsIgnoreCase("Nay")) return Boolean.FALSE;
				throw new IllegalArgumentException("Cannot convert value '"+value+"' to a java.lang.Boolean");
			}
			if(value instanceof Number) {
				return (((Number)value).intValue() != 0) ? Boolean.TRUE : Boolean.FALSE;
			}			
		} else if (aClass.isAssignableFrom(Byte.class)) {
			if (value instanceof String) return Byte.valueOf((String)value);
			if (value instanceof Number) return new Byte(((Number)value).byteValue());
			if (value instanceof Boolean) return new Byte(((Boolean)value).booleanValue() ? BOOLEAN_TRUE_NUMERIC : BOOLEAN_FALSE_NUMERIC);
		} else if (aClass.isAssignableFrom(Short.class)) {
			if (value instanceof String) return Short.valueOf((String)value);
			if (value instanceof Number) return new Short(((Number)value).shortValue());
			if (value instanceof Boolean) return new Short(((Boolean)value).booleanValue() ? BOOLEAN_TRUE_NUMERIC : BOOLEAN_FALSE_NUMERIC);
		} else if (aClass.isAssignableFrom(Integer.class)) {
			if (value instanceof String) return Integer.valueOf((String)value);
			if (value instanceof Number) return new Integer(((Number)value).intValue());
			if (value instanceof Boolean) return new Integer(((Boolean)value).booleanValue() ? BOOLEAN_TRUE_NUMERIC : BOOLEAN_FALSE_NUMERIC);
		} else if (aClass.isAssignableFrom(Long.class)) {
			if (value instanceof String) return Long.valueOf((String)value);
			if (value instanceof Number) return new Long(((Number)value).longValue());
			if (value instanceof Boolean) return new Long(((Boolean)value).booleanValue() ? BOOLEAN_TRUE_NUMERIC : BOOLEAN_FALSE_NUMERIC);
		} else if (aClass.isAssignableFrom(Float.class)) {
			if (value instanceof String) return Float.valueOf((String)value);
			if (value instanceof Number) return new Float(((Number)value).floatValue());
			if (value instanceof Boolean) return new Float(((Boolean)value).booleanValue() ? BOOLEAN_TRUE_NUMERIC : BOOLEAN_FALSE_NUMERIC);
		} else if (aClass.isAssignableFrom(Double.class)) {
			if (value instanceof String) return Double.valueOf((String)value);
			if (value instanceof Number) return new Double(((Number)value).doubleValue());
			if (value instanceof Boolean) return new Double(((Boolean)value).booleanValue() ? BOOLEAN_TRUE_NUMERIC : BOOLEAN_FALSE_NUMERIC);
		} else if (aClass.isAssignableFrom(Character.class)) {
			if (value instanceof String) return new Character(((String)value).charAt(0));
			if (value instanceof Number) return new Character((char)((Number)value).byteValue());
			if (value instanceof Boolean) return new Character(((Boolean)value).booleanValue() ? 'T' : 'F');
		}

		if (value instanceof String) {
			String stringVal = (String)value;
			if (Float.class.isAssignableFrom(aClass)) return new Float((String)value);
			if (Double.class.isAssignableFrom(aClass)) return new Double((String)value);
			if (Byte.class.isAssignableFrom(aClass)) return new Byte((String)value);
			if (Short.class.isAssignableFrom(aClass)) return new Short((String)value);
			if (Integer.class.isAssignableFrom(aClass)) return new Integer((String)value);
			if (Long.class.isAssignableFrom(aClass)) return new Long((String)value);
			if (Character.class.isAssignableFrom(aClass)) return new Character(stringVal.charAt(0));
		} 

		if (converter != null) return converter.convert(value,aClass); 
		throw new IllegalArgumentException("Don't know how to convert a String to a " + aClass);
	}

	public static Object convert(boolean object, Class<?> aClass) {
		return convert(new Boolean(object),aClass);
	}	
	
	public static Object convert(byte object, Class<?> aClass) {
		return convert(new Byte(object),aClass);
	}	
	
	public static Object convert(short object, Class<?> aClass) {
		return convert(new Short(object),aClass);
	}	
	
	public static Object convert(int object, Class<?> aClass) {
		return convert(new Integer(object),aClass);
	}	
	
	public static Object convert(long object, Class<?> aClass) {
		return convert(new Long(object),aClass);
	}	
	
	public static Object convert(float object, Class<?> aClass) {
		return convert(new Float(object),aClass);
	}	
	
	public static Object convert(double object, Class<?> aClass) {
		return convert(new Double(object),aClass);
	}	
	
	public static Object convert(char object, Class<?> aClass) {
		return convert(new Character(object),aClass);
	}	
	
	public static boolean convertToBoolean(Object object) {
		return ((Boolean)convert(object,Boolean.class)).booleanValue();
	}	
	
	public static byte convertToByte(Object object) {
		return ((Number)convert(object,Byte.class)).byteValue();
	}	
	
	public static short convertToShort(Object object) {
		return ((Number)convert(object,Short.class)).shortValue();
	}	
	
	public static int convertToInt(Object object) {
		return ((Number)convert(object,Integer.class)).intValue();
	}	
	
	public static long convertToLong(Object object) {
		return ((Number)convert(object,Long.class)).longValue();
	}	
	
	public static float convertToFloat(Object object) {
		return ((Number)convert(object,Float.class)).floatValue();
	}	
	
	public static double convertToDouble(Object object) {
		return ((Number)convert(object,Double.class)).doubleValue();
	}	
	
	public static char convertToChar(Object object) {
		return ((Character)convert(object,Character.class)).charValue();
	}	

}
